﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LogLib;
using System.Threading;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace Mp3Player
{
    public class Speaker
    {
        public string VoiceDirectory
        {
            get;
            set;
        }

        /// <summary>
        /// 存储提示信息
        /// </summary>
        private ConcurrentStack<string> _stackTips = new ConcurrentStack<string>();

        /// <summary>
        /// 存储需要连续播放的提示信息
        /// </summary>
        private ConcurrentQueue<string> _qPrompts = new ConcurrentQueue<string>();

        protected FormMessageInteraction _formComm = null;

        protected IntPtr WindowHandle = IntPtr.Zero;

        private Stopwatch sw = new Stopwatch();

        public bool IsSpeaking
        {
            private set;
            get;
        }

        public Speaker()
        {
            IsSpeaking = false;
            CreatCommunicationForm();
        }

        public void Abort()
        {
            // 首先清空所有任务
            string file;
            while (!_qPrompts.IsEmpty)
            {
                _qPrompts.TryDequeue(out file);
            }
            _stackTips.Clear();
            Stop();
        }

        /// <summary>
        /// 创建用于和底层控制通信的窗口
        /// </summary>
        /// <returns></returns>
        protected void CreatCommunicationForm()
        {
            _formComm = new FormMessageInteraction();
            _formComm.SpeakingDone += OnSpeakingDone;
            _formComm.Show();
            WindowHandle = _formComm.Handle;
        }

        public event QueueSpeakingDoneEventHandler QueueSpeakingDone;
        protected void OnQueueSpeakingDone()
        {
            if (QueueSpeakingDone != null)
            {
                QueueSpeakingDone(this, new QueueSpeakingDoneEventArgs(sw.ElapsedMilliseconds));
            }
        }

        public void RemoveSpeakingDoneEvent()
        {
            QueueSpeakingDone = null;
        }

        protected void OnSpeakingDone(object sender, EventArgs e)
        {
            if (!_qPrompts.IsEmpty)
            {
                ProcessingQueue();
                return;
            }
            else
            {
                IsSpeaking = false;
                sw.Stop();
                //只有语音调用页面和当前页面匹配才执行
                OnQueueSpeakingDone();
                ProcessingStack();
            }
        }

        /// <summary>
        /// 界面和语音提示
        /// </summary>
        protected void OnProcessing()
        {
            try
            {
                ProcessingQueue();
                ProcessingStack();
            }
            catch
            {
                //Log.WriteException("界面及语音提示工作项发生了异常: " + e.ToString());
            }
        }

        private void ProcessingQueue()
        {
            string file;
            if (_qPrompts.TryDequeue(out file))
            {
                IsSpeaking = true;
                OnSpeaking(file);
            }
        }

        private void ProcessingStack()
        {
            string file;
            if (!_stackTips.IsEmpty)
            {
                if (_stackTips.TryPop(out file))
                {
                    IsSpeaking = true;
                    OnSpeaking(file);
                    _stackTips.Clear();
                }
            }
        }

        public void AddList(List<string> list, bool clearFlag = false)
        {
            if (clearFlag)
            {
                while (!_qPrompts.IsEmpty)
                {
                    string item;
                    if (_qPrompts.TryDequeue(out item))
                    {
                    }
                }
            }
            foreach (string val in list)
            {
                _qPrompts.Enqueue(val);
            }
            sw.Restart();
            ProcessingQueue();
        }

        public void Add(string item, bool onForced = false)
        {
            if (onForced)
            {
                _stackTips.Clear();
            }
            _stackTips.Push(item);
            ProcessingStack();
        }

        public void Reset()
        {
            while (!_qPrompts.IsEmpty)
            {
                string item;
                if (_qPrompts.TryDequeue(out item))
                {
                }
            }
            _stackTips.Clear();
        }

        public void OnSpeaking(string strInfo)
        {
            string voiceFile = strInfo;
            if (File.Exists(voiceFile))
            {
                Play(voiceFile, WindowHandle);
                return;
            }
            if (VoiceDirectory.IndexOf('\\') >= 0 || VoiceDirectory.IndexOf('/') >= 0)
            {
                voiceFile = $"{VoiceDirectory}\\{strInfo}.mp3";
                if (File.Exists(voiceFile))
                {
                    Play(voiceFile, WindowHandle);
                    return;
                }
                voiceFile = $"{VoiceDirectory}\\Voice\\{strInfo}.mp3";
                if (File.Exists(voiceFile))
                {
                    Play(voiceFile, WindowHandle);
                    return;
                }
            }
            Log.WriteAsync("未能找到语音录音文件: " + strInfo);
        }


        #region API
        /// <summary>
        /// 播放声音文件
        /// </summary>
        /// <param name="file"></param>
        public static void Play(string file)
        {
            if (File.Exists(file))
            {
                mciSendString("close all", "", 0, IntPtr.Zero);
                mciSendString("open " + file + " alias media notify", "", 0, IntPtr.Zero);
                mciSendString("play media", "", 0, IntPtr.Zero);
            }
        }


        /// <summary>
        /// 播放声音文件
        /// </summary>
        /// <param name="file"></param>
        public static void PlaySync(string file)
        {
            if (File.Exists(file))
            {
                //mciSendString("close all", "", 0, IntPtr.Zero);
                mciSendString("open " + file + " alias media", "", 0, IntPtr.Zero);
                mciSendString("play media wait", "", 0, IntPtr.Zero);
                //mciSendString("close media", "", 0, IntPtr.Zero);
            }
        }

        private static string _CurPlayFileName = "";
        private static string _AliasName = "";
        //private static WaveOut _Player = new WaveOut();
        /// <summary>
        /// 播放声音文件
        /// </summary>
        /// <param name="file"></param>
        public static void Play(string file, IntPtr WindowHandle)
        {
            if (File.Exists(file))
            {
                //上次播放未结束
                if (GetPlayerStatus() == "playing")
                {
                    if (!string.IsNullOrEmpty(_CurPlayFileName))
                    {
                        //如果传进来的文件跟上次播放的不一样，就停止播放当前的，再播放新的
                        if (_CurPlayFileName != file)
                        {
                            mciSendString($"stop {_AliasName}", null, 0, IntPtr.Zero);
                        }
                        else
                        {
                            //如果传进来的文件跟上次播放的一样，就继续播放
                            return;
                        }
                    }
                }

                _CurPlayFileName = file; _AliasName = $"nmci{DateTime.Now.Ticks}";
                mciSendString($"open {file} alias {_AliasName}", null, 0, IntPtr.Zero);
                mciSendString($"play {_AliasName}", "", 0, IntPtr.Zero);
            }
            else
            {
                string msg = "不存在的语音文件:" + file;
                Log.WriteAsync(msg);
                throw new Exception(msg);
            }
        }

        private static string GetPlayerStatus()
        {

            if (string.IsNullOrEmpty(_AliasName))
            {
                return "stopped";
            }
            string buffer = new string('\0', 32);
            mciSendString($"status {_AliasName} mode", buffer, 32, IntPtr.Zero);
            string state = buffer.TrimEnd('\0').ToLower();
            return state;


        }


        /// <summary>   
        /// 暂停   
        /// </summary>   
        public static void Pause()
        {
            mciSendString("pause media", "", 0, IntPtr.Zero);
        }

        /// <summary>   
        /// 停止   
        /// </summary>   
        public static void Stop()
        {
            mciSendString("close media", "", 0, IntPtr.Zero);
        }

        /// <summary>   
        /// API函数   
        /// </summary>   
        [DllImport("winmm.dll", EntryPoint = "mciSendString", CharSet = CharSet.Auto)]
        private static extern int mciSendString(
            string lpstrCommand,
            string lpstrReturnString,
            int uReturnLength,
            IntPtr hwndCallback
        );
        #endregion    
    }
}
